/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦]
 * 
 * https://zhiqim.org/project/zhiqim_framework/zhiqim_httpd.htm
 *
 * Zhiqim Httpd is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.httpd.sessioner;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import org.zhiqim.httpd.HttpSession;
import org.zhiqim.httpd.HttpSessionUser;
import org.zhiqim.httpd.model.HttpDomainPortPath;
import org.zhiqim.kernel.model.maps.HashMapSV;
import org.zhiqim.kernel.paging.PageBuilder;
import org.zhiqim.kernel.paging.PageResult;
import org.zhiqim.kernel.schedule.Interval;
import org.zhiqim.kernel.schedule.Task;
import org.zhiqim.kernel.util.Asserts;
import org.zhiqim.kernel.util.Ids;
import org.zhiqim.kernel.util.Lists;

/**
 * Hash会话管理器，通过HashMap对会话进行管理
 *
 * @version v1.0.0 @author zouzhigang 2014-3-21 新建与整理
 */
public class HashSessionManager extends AbsSessionManager implements Task
{
    private int sessionTimeout = 30 * 60;
    private String sessionIdName = _JSESSIONID_;
    private HashMapSV<HashSession> sessionMap;
    private Interval every;
    
    /**************************************************************************************/
    //开启和关闭
    /**************************************************************************************/
    
    @Override /** 开启会话管理器 */
    public void open()
    {
        this.sessionMap = new HashMapSV<>(999, true);
        this.every = Interval.shedule(this, 29 * 1000);//定义29秒检查一次，保证最小1分钟清除即可，太频繁也不大合适
    }
    
    @Override /** 关闭会话管理器 */
    public void close()
    {
        if (this.every != null){
            this.every.close();
            this.every = null;
        }
        
        this.sessionMap.clear();
        this.sessionMap = null;
    }
    
    /**************************************************************************************/
    //设置参数部分
    /**************************************************************************************/
    
    @Override /** 设置会话编号名称 */
    public void setSessionIdName(String sessionIdName)
    {
        this.sessionIdName = sessionIdName;
    }
    
    @Override /** 获取会话编号名称 */
    public String getSessionIdName()
    {
        return sessionIdName;
    }
        
    @Override /** 设置会话超时时长 */
    public void setSessionTimeout(int sessionTimeout)
    {
        this.sessionTimeout = sessionTimeout;
    }
    
    @Override /** 获取超时时长 */
    public int getSessionTimeout()
    {
        return sessionTimeout;
    }
    
    @Override /** 设置会话管理器参数，Hash模式无需参数 */
    public void setSessionParameter(String sessionParameter)
    {
    }
    
    /**************************************************************************************/
    //新建 & 保存 & 获取 & 失效 & 更新会话
    /**************************************************************************************/
    
    @Override /** 新建会话 */
    public HttpSession newSession(HttpDomainPortPath domainPortPath, String sessionIp, String userAgent)
    {
        String mark = (sessionIdMark != null)?sessionIdMark:domainPortPath.buildSessionIdMark();
        return new HashSession(Ids.uuid() + mark, sessionIp, userAgent, this);
    }
    
    @Override /** 保存会话 */
    public void setSession(HttpSession session)
    {
        sessionMap.put(session.getId(), (HashSession)session);
    }
    
    @Override /** 通过会话编号获取会话对象 */
    public HttpSession getSession(String sessionId)
    {
        return sessionMap.get(sessionId);
    }
    
    @Override /** 判断会话是否存在 */
    public boolean hasSession(String sessionId)
    {
        return sessionMap.containsKey(sessionId);
    }
    
    @Override /** 删除一个会话，指定会话编号 */
    public void invalidateSession(String sessionId)
    {
        HttpSession session = sessionMap.remove(sessionId);
        if (session == null)
            return;
        
        session.invalidate();
        session = null;
    }
    
    @Override /** 更新最后访问时间 */
    public void updateLastAccessed(String sessionId)
    {
        HashSession session = sessionMap.get(sessionId);
        if (session == null)
            return;
        
        session.updateLastAccessed();
    }
    
    @Override /** 更新信息 */
    public void updateInfo(String sessionId, String sessionIp, String userAgent)
    {
        HashSession session = sessionMap.get(sessionId);
        if (session == null)
            return;
        
        session.updateInfo(sessionIp, userAgent);
    }
    
    @Override /** 更新会话并返回会话 */
    public HttpSession getSessionByUpdate(String sessionId, String sessionIp, String userAgent)
    {
        HashSession session = sessionMap.get(sessionId);
        if (session == null)
            return null;
        
        session.updateInfo(sessionIp, userAgent);
        return session;
    }
    
    /**************************************************************************************/
    //会话查询和统计
    /**************************************************************************************/
    
    @Override /** 查会话数 */
    public int size()
    {
        return sessionMap.size();
    }
    
    @Override /** 获取会话列表 */
    public List<HttpSession> getSessionList()
    {
        ArrayList<HttpSession> list = new ArrayList<>();
        for (HttpSession s : sessionMap.values())
        {
            list.add(s);
        }
    
        return Collections.unmodifiableList(Lists.trim(list));
    }
    
    @Override /** 获取会话分页信息 */
    public PageResult<HttpSession> getSessionPage(int pageNo, int pageSize)
    {
        int size = sessionMap.size();
        if (size == 0)
            return PageBuilder.newResult(pageNo, pageSize);
        
        int pageNoMax = (size-1) / pageSize + 1;
        if (pageNo < 1) pageNo = 1;
        if (pageNo > pageNoMax) pageNo = pageNoMax;
        
        int start = (pageNo-1) * pageSize;
        int end = pageNo * pageSize;
        
        ArrayList<HttpSession> list = new ArrayList<HttpSession>();
        Iterator<HashSession> it = sessionMap.values().iterator();
        for (int i=0;it.hasNext();i++)
        {
            HttpSession s = it.next();
            if (i < start)
                continue;
            else if (i >= end)
                break;
            else
                list.add(s);
        }
        
        list.trimToSize();
        return PageBuilder.newResult(size, pageNo, pageSize, list);
    }
    
    /**************************************************************************************/
    //会话用户查询和统计
    /**************************************************************************************/
    
    @Override /** 查用户数，有登录状态 */
    public int sizeUser()
    {
        int size = 0;
        for (HttpSession s : sessionMap.values())
        {
            if (s.hasSessionUser())
                size++;
        }

        return size;
    }
    
    @Override /** 是否有会话名称对应的会话 */
    public boolean hasSessionName(String sessionName)
    {
        Asserts.notNull(sessionName, "sessionName");
        
        for (HttpSession s : sessionMap.values())
        {
            HttpSessionUser sessionUser = s.getSessionUser();
            if (sessionUser == null)
                continue;
            
            if (sessionName.equals(sessionUser.getSessionName()))
                return true;
        } 
        
        return false;
    }
    
    @Override /** 获取会话登录用户列表，指定会话用户类名（如前后端共用session管理器的情况） */
    public List<HttpSessionUser> getSessionUserList()
    {
        List<HttpSessionUser> list = new ArrayList<>();
        for (HttpSession s : sessionMap.values())
        {
            HttpSessionUser sessionUser = s.getSessionUser();
            if (sessionUser != null)
                list.add(sessionUser);
        }
    
        return Collections.unmodifiableList(Lists.trim(list));
    }
    
    @Override /** 获取会话登录用户分页信息，指定会话用户类名（如前后端共用session管理器的情况） */
    public PageResult<HttpSessionUser> getSessionUserPage(int pageNo, int pageSize)
    {
        if (pageNo < 1) pageNo = 1;
        int start = (pageNo-1) * pageSize;
        int end = pageNo * pageSize;
        
        int total = 0;
        List<HttpSessionUser> list = new ArrayList<>();
        for (HttpSession s : sessionMap.values())
        {
            HttpSessionUser sessionUser = s.getSessionUser();
            if (sessionUser == null)
                continue;
            
            total++;
            if (total <= start || total > end)
                continue;//非当前页
            else
                list.add(sessionUser);
        }

        list = Collections.unmodifiableList(Lists.trim(list));
        return PageBuilder.newResult(total, pageNo, pageSize, list);
    }
    
    /**************************************************************************************/
    //定时任务
    /**************************************************************************************/
    
    @Override /** 定时检查超时用户 */
    public void execute()
    {
        for (Iterator<HashSession> it=sessionMap.values().iterator();it.hasNext();)
        {
            HttpSession session = it.next();
            if (session.getMaxInactiveInterval() <= 0)
                continue;//设置为0或负数表示永驻内存
            
            if(System.currentTimeMillis()-session.getLastAccessed() < session.getMaxInactiveInterval() * 1000)
                continue;//未超时
            
            //已超时清理
            it.remove();
            session.invalidate();
            session = null;
        }
    }
}
